﻿using System;
using System.Drawing;
using System.Media;
using System.Windows.Forms;
using Tetris.Properties;

namespace Tetris {
    // 游戏
    class Game {
        // 回调
        public interface Callback {
            void OnDifficultyUp(int difficulty);    // 难度增加事件
            void OnGameOver(bool isWon);            // 游戏结束事件
            void OnScore(int score);                // 得分事件
        }

        // 状态
        public enum Status {
            END,    // 已结束
            PAUSED, // 暂停中
            PLAYING // 游戏中
        }

        public Status CurrentStatus { get; private set; }   // 当前状态
        private readonly Callback mCallback;                // 回调
        private Block mCurBlock;                            // 当前方块
        private int mDifficulty;                            // 难度
        private bool mEnableMusic;                          // 音乐开关
        private readonly Control mGameBoard;                // 游戏区域
        private readonly Brush mGameBoardBrush;             // 游戏区域背景画刷
        private readonly Graphics mGameBoardDrawer;         // 游戏区域绘图者
        private readonly SoundPlayer mMusicPlayer;          // 音乐播放者
        private Block mNextBlock;                           // 下一个方块
        private readonly Control mPreview;                  // 预览区域
        private readonly Brush mPreviewBursh;               // 预览区域背景画刷
        private readonly Graphics mPreviewDrawer;           // 预览区域绘图者
        private readonly Random mRandom;                    // 随机数生成器
        private int mScore;                                 // 得分
        private readonly Timer mTimer;                      // 定时器
        private int mTop;                                   // 有方块的最高行
        public static ColorGrid[,] sExistentBlocks;         // 累积的方块

        /**
         * 构造方法。
         * @param callback          回调
         * @param gameBoard         游戏区域
         * @param gameBoardDrawer   游戏区域绘图者
         * @param preview           预览区域
         * @param previewDrawer     预览区域绘图者
         */
        public Game(Callback callback, Control gameBoard, Graphics gameBoardDrawer, Control preview, Graphics previewDrawer) {
            // 初始化成员
            CurrentStatus = Status.END;
            mCallback = callback;
            mDifficulty = 1;
            mEnableMusic = false;
            mGameBoard = gameBoard;
            mGameBoardBrush = new SolidBrush(Const.GAME_BOARD_COLOR);
            mGameBoardDrawer = gameBoardDrawer;
            mMusicPlayer = new SoundPlayer(Resources.music);
            mPreview = preview;
            mPreviewBursh = new SolidBrush(Const.PREVIEW_COLOR);
            mPreviewDrawer = previewDrawer;
            mRandom = new Random();
            mScore = 0;
            mTimer = new Timer();
            mTimer.Tick += new EventHandler(OnTimer);
            mTimer.Interval = Const.TIMER_INIT;
            mTop = Const.GAME_BOARD_GRID_COUNT_V + 1;

            // 画游戏区域
            this.DrawGameBoard();

            // 画预览区域
            this.DrawPreview();
        }

        /**
         * 检查是否存在满行。
         */
        private void CheckRow() {
            // 更新"有方块的最高行"
            int top = mCurBlock.GetTop();
            if (top < mTop) {
                mTop = top;
            }

            int released = 0;   // 消除的行数
            int bottom = mCurBlock.GetBottom();

            // 从有方块的最上面一行，到当前方块所占的最下一行，逐行检查
            for (int i = mTop; i <= bottom; i++) {
                for (int j = 0; j < Const.GAME_BOARD_GRID_COUNT_H; j++) {
                    // 如果该行有空的，就跳过该行
                    if (!sExistentBlocks[i, j].mUsed) {
                        break;
                    }
                    // 如果该行满了，消除该行
                    if (j == Const.GAME_BOARD_GRID_COUNT_H - 1) {
                        this.ReleaseRow(i);
                        released++;
                    }
                }
            }

            if (released > 0) {
                // 因为消行了，所以需要重绘
                this.DrawGameBoard();
                this.DrawExistentBlock();

                // 计算得分
                mScore += released * released * 100;

                // 发送通知
                mCallback.OnScore(mScore);

                // 计算难度
                if (mScore / Const.DIFF_BASE + 1 > mDifficulty && mDifficulty < Const.DIFF_MAX) {
                    // 增加难度
                    mDifficulty++;
                    mTimer.Stop();
                    mTimer.Interval = Const.TIMER_INIT - Const.TIMER_GROW * (mDifficulty - 1);
                    mTimer.Start();

                    // 发送通知
                    mCallback.OnDifficultyUp(mDifficulty);
                }
            }
        }

        /**
         * 随机创建一种方块。
         * @return 方块
         */
        private Block CreateRandBlock() {
            int random = mRandom.Next(1, 8);

            switch (random) {
                case 1:
                    return new Block1();
                case 2:
                    return new Block2();
                case 3:
                    return new Block3();
                case 4:
                    return new Block4();
                case 5:
                    return new Block5();
                case 6:
                    return new Block6();
                case 7:
                    return new Block7();             
            }

            return new Block1();
        }

        /**
         * 画累积的方块。
         */
        private void DrawExistentBlock() {
            for (int i = 0; i < Const.GAME_BOARD_GRID_COUNT_V; i++) {
                for (int j = 0; j < Const.GAME_BOARD_GRID_COUNT_H; j++) {
                    if (sExistentBlocks[i, j].mUsed) {
                        Rectangle rect = new Rectangle(Const.GRID_SIZE * j, Const.GRID_SIZE * i, Const.GRID_SIZE, Const.GRID_SIZE);
                        mGameBoardDrawer.FillRectangle(new SolidBrush(sExistentBlocks[i, j].mColor), rect);
                        Block.DrawEdge(mGameBoardDrawer, rect);
                    }
                }
            }
        }

        /**
         * 画游戏区域。
         */
        private void DrawGameBoard() {
            mGameBoardDrawer.FillRectangle(mGameBoardBrush, 0, 0, Const.GAME_BOARD_WIDTH, Const.GAME_BOARD_HEIGHT);
            mGameBoard.Invalidate();
        }

        /**
         * 画预览区域。
         */
        private void DrawPreview() {
            mPreviewDrawer.FillRectangle(mPreviewBursh, 0, 0, Const.PREVIEW_WIDTH, Const.PREVIEW_HEIGHT);
            if (mNextBlock != null) {
                mNextBlock.EnterPreview(mPreviewDrawer);
            }
            mPreview.Invalidate();
        }

        /**
         * 游戏结束。
         * @param isWon  胜利则为 true，否则为 false
         */
        private void GameOver(bool isWon) {
            // 将状态设置为已结束
            CurrentStatus = Status.END;

            // 停止定时器
            mTimer.Stop();

            // 停止播放音乐
            if (mEnableMusic) {
                mMusicPlayer.Stop();
            }

            // 发送通知
            mCallback.OnGameOver(isWon);
        }

        /**
         * 判断是否失败。
         * @return 失败则返回 true，否则返回 false
         */
        private bool IsLost() {
            return mTop == 0 && sExistentBlocks[0, 4].mUsed;
        }

        /**
         * 判断是否胜利。
         * @return 胜利则返回 true，否则返回 false
         */
        private bool IsWon() {
            return mScore >= Const.WIN_NUMBER;
        }

        /**
         * 方块下落。
         */
        private void MoveBlockDown() {
            if (CurrentStatus != Status.PLAYING) {
                return;
            }

            bool canMoveDown = mCurBlock.MoveDown();
            if (!canMoveDown) {
                // 检查是否存在满行
                this.CheckRow();

                // 判断是否胜利
                if (this.IsWon()) {
                    this.GameOver(true);
                    return;
                }

                // 判断是否失败
                if (this.IsLost()) {
                    this.GameOver(false);
                    return;
                }

                // 产生新的方块
                this.NextBlock();
            }
        }

        /**
         * 产生新的方块。
         */
        private void NextBlock() {
            // 创建当前方块
            if (mCurBlock != null) {
                mCurBlock = mNextBlock;
            } else {
                mCurBlock = this.CreateRandBlock();
            }

            // 创建下一个方块
            mNextBlock = this.CreateRandBlock();

            // 当前方块进入游戏区域
            mCurBlock.EnterGameboard(mGameBoard, mGameBoardDrawer, mGameBoardBrush);

            // 下一个方块进入预览区域
            this.DrawPreview();
        }

        /**
         * 按钮事件的响应方法。
         * @param key   按键
         * @return 事件在此被处理则返回 true，否则返回 false
         */
        public bool OnKeyPressed(Keys key) {
            if (CurrentStatus != Status.PLAYING) {
                return false;
            }

            switch (key) {
                case Keys.Down:
                    this.MoveBlockDown();
                    return true;
                case Keys.Left:
                    mCurBlock.MoveLeft();
                    return true;
                case Keys.Right:
                    mCurBlock.MoveRight();
                    return true;
                case Keys.Up:
                    mCurBlock.Transform();
                    return true;
            }
            return false;
        }

        /*
         * 定时器事件的响应方法。
         * @param sender    事件发送者
         * @param e         事件相关信息
         */
        private void OnTimer(object sender, EventArgs e) {
            this.MoveBlockDown();
        }

        /**
         * 游戏暂停。
         */
        public void Pause() {
            if (CurrentStatus == Status.PLAYING) {
                // 停止定时器
                mTimer.Stop();

                // 停止播放音乐
                if (mEnableMusic) {
                    mMusicPlayer.Stop();
                }

                // 将状态设置为已暂停
                CurrentStatus = Status.PAUSED;
            } else if (CurrentStatus == Status.PAUSED) {
                // 开启定时器
                mTimer.Start();

                // 播放音乐
                if (mEnableMusic) {
                    mMusicPlayer.PlayLooping();
                }

                // 将状态设置为游戏中
                CurrentStatus = Status.PLAYING;
            }
        }

        /**
         * 播放音乐。
         * @return 正在播放音乐则为 true，否则为 false
         */
        public bool PlayMusic() {
            mEnableMusic = !mEnableMusic;

            if (mEnableMusic) {
                if (CurrentStatus == Status.PLAYING) {
                    mMusicPlayer.PlayLooping();
                }
            } else {
                mMusicPlayer.Stop();
            }

            return mEnableMusic;
        }

        /**
         * 消行。
         * @param row  行号
         */
        private void ReleaseRow(int row) {
            // 最上行
            if (row == 0) {
                for (int j = 0; j < Const.GAME_BOARD_GRID_COUNT_H; j++) {
                    sExistentBlocks[row, j].mUsed = false;
                }
                mTop++;
                return;
            }

            // 非最上行
            for (int i = row; i > mTop; i--) {
                for (int j = 0; j < Const.GAME_BOARD_GRID_COUNT_H; j++) {
                    sExistentBlocks[i, j].mUsed = sExistentBlocks[i - 1, j].mUsed;
                    sExistentBlocks[i, j].mColor = sExistentBlocks[i - 1, j].mColor;
                }
            }

            for (int j = 0; j < Const.GAME_BOARD_GRID_COUNT_H; j++) {
                sExistentBlocks[mTop, j].mUsed = false;
            }

            mTop++;
        }

        /**
         * 游戏开始。
         */
        public void Start() {
            // 画游戏区域
            this.DrawGameBoard();

            // 画预览区域
            this.DrawPreview();

            // 创建方块
            sExistentBlocks = new ColorGrid[Const.GAME_BOARD_GRID_COUNT_V, Const.GAME_BOARD_GRID_COUNT_H];
            for (int i = 0; i < Const.GAME_BOARD_GRID_COUNT_V; i++) {
                for (int j = 0; j < Const.GAME_BOARD_GRID_COUNT_H; j++) {
                    sExistentBlocks[i, j] = new ColorGrid();
                }
            }
            mCurBlock = null;
            mNextBlock = null;
            this.NextBlock();

            // 重置难度
            mDifficulty = 1;

            // 重置分数
            mScore = 0;

            // 开启定时器
            mTimer.Stop();
            mTimer.Interval = Const.TIMER_INIT;
            mTimer.Start();

            // 播放音乐
            if (mEnableMusic) {
                mMusicPlayer.PlayLooping();
            }

            // 将状态设置为游戏中
            CurrentStatus = Status.PLAYING;
        }
    }
}
